/*
 * main.c - cve-check-tool
 *
 * Copyright (C) 2015 Intel Corporation
 *
 * cve-check-tool is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#define _GNU_SOURCE
#include <curl/curl.h>
#include <dirent.h>
#include <errno.h>
#include <gio/gio.h>
#include <glib.h>
#include <libgen.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "cve-check-tool.h"

#include "config.h"
#include "core.h"
#include "cve-db-lock.h"
#include "cve-string.h"
#include "plugins/packaging/faux/faux.h"
#include "util.h"

#include "update.h"

#include "plugin-manager.h"

typedef struct CveToolInstance {
        CveCheckTool shared;   /*<Public exposed data */
        CvePlugin *pkg_plugin; /*<Our current plugin */
} CveToolInstance;

static CveCheckTool *self;
static CveToolInstance *self_priv;

static char *srpm_dir = NULL;

#define DEFAULT_CONFIG_FILE DEFAULT_PATH "/cve-check-tool.conf"
#define SITE_CONFIG_FILE SITE_PATH "/cve-check-tool.conf"

/**
 * Helper utility to free a struct source_package_t
 */
static inline void package_free(void *p)
{
        if (!p) {
                return;
        }
        struct source_package_t *t = p;
        CvePlugin *pkg_plugin = self_priv->pkg_plugin;

        if (t->extra && pkg_plugin && pkg_plugin->free_package) {
                pkg_plugin->free_package(t);
                t->extra = NULL;
        }

        if (t->issues) { /* bless you */
                g_list_free_full(t->issues, xmlFree);
        }
        if (t->patched) {
                g_list_free_full(t->patched, xmlFree);
        }
        if (t->path) {
                free(t->path);
        }
        if (t->xml) {
                xmlFree((xmlChar *)t->name);
                xmlFree((xmlChar *)t->version);
        } else {
                g_free((gchar *)t->name);
                g_free((gchar *)t->version);
        }

        free(t);
}

static void cve_add_package_internal(struct source_package_t *pkg)
{
        GList *issues = NULL, *em = NULL;
        gchar *cur_id = NULL;
        gchar *q = NULL;
        CvePlugin *pkg_plugin = self_priv->pkg_plugin;

        printf("cve_add_package_internal\n");
        if (!pkg) {
                return;
        }

        if (g_hash_table_contains(self->db, pkg->name)) {
                package_free(pkg);
                return;
        }

        if (self->mapping) {
                q = g_hash_table_lookup(self->mapping, pkg->name);

                /*
                 * overwrite the version from the initial package list
                 * by the one provided in the mapping file
                 *
                 * [Mapping]
                 * <NAME FOUND IN NVD>,<VERSION FOUND IN NVD> = <OWN PKG NAME>
                 *
                 * for example
                 *
                 * [Mapping]
                 * linux_kernel,4.3 = my_own_linux_kernel_name
                 *
                 */
                gchar **temp = g_strsplit(q, ",", -1);
                if (temp[1] != NULL) {
                        g_free(q);
                        q = g_strndup(temp[0], strlen(temp[0]));
                        g_free(pkg->version);
                        pkg->version = g_strndup(temp[1], strlen(temp[1]));
                        g_strfreev(temp);
                }
        }

        if (use_frac_compare) {
                issues = cve_db_get_issues_frac_compare(self->cve_db, q ? q : pkg->name, pkg->version);
        } else {
                issues = cve_db_get_issues(self->cve_db, q ? q : pkg->name, pkg->version);
        }

        if (!issues) {
                goto insert;
        }

        for (em = issues; em; em = em->next) {
                cur_id = em->data;

                if (!cur_id) {
                        fprintf(stderr,
                                "Fatal configuration detected (null immutable item): Please report this issue to: "
                                "https://github.com/ikeydoherty/cve-check-tool/issues/20\n");
                        continue;
                }

                if (pkg_plugin->is_ignored && pkg_plugin->is_ignored(pkg, (gchar *)cur_id)) {
                        continue;
                }

                if (pkg_plugin->is_patched && pkg_plugin->is_patched(pkg, (gchar *)cur_id)) {
                        if (!g_list_find_custom(pkg->patched, cur_id, (GCompareFunc)strcmp)) {
                                gchar *tmp = g_strdup((const gchar *)cur_id);
                                if (!tmp) {
                                        abort();
                                }
                                pkg->patched = g_list_append(pkg->patched, tmp);
                        }
                } else {
                        if (!g_list_find_custom(pkg->issues, cur_id, (GCompareFunc)strcmp)) {
                                gchar *tmp = g_strdup((const gchar *)cur_id);
                                if (!tmp) {
                                        abort();
                                }
                                pkg->issues = g_list_append(pkg->issues, tmp);
                        }
                }
        }
        g_list_free_full(issues, g_free);
insert:
        g_hash_table_insert(self->db, pkg->name, pkg);
}

static inline bool csv_string_empty(const char *p)
{
        return (g_str_equal(p, "") || g_str_equal(p, ","));
}

/**
 * Load CSV data, "faux" as this is metadata, not generated from scanned source
 * files.
 */
static bool load_faux(const char *path)
{
        FILE *fp = NULL;
        size_t read = -1;
        size_t buf_size = 0;
        char *buf = NULL;
        bool ret = false;
        int line = 1;

        fp = fopen(path, "r");
        if (!fp) {
                fprintf(stderr, "load_faux(): %s\n", strerror(errno));
                goto end;
        }

        while ((read = getline(&buf, &buf_size, fp) > 0)) {
                /* TODO: Use a dedicated CsvParser */
                autofree(gstrv) *strv = NULL;
                struct source_package_t *t = NULL;
                struct FauxData *d = NULL;
                gint len;

                buf = g_strchomp(buf);
                /* Empty line */
                if (streq(buf, "")) {
                        goto next;
                }

                strv = g_strsplit(buf, ",", 4);
                if ((len = g_strv_length(strv)) != 4) {
                        fprintf(stderr, "Line #%d is of incorrect length\n", line);
                        break;
                }

                strv[0] = g_strstrip(strv[0]);
                strv[1] = g_strstrip(strv[1]);
                strv[2] = g_strstrip(strv[2]);
                strv[3] = g_strstrip(strv[3]);

                if (csv_string_empty(strv[0])) {
                        fprintf(stderr, "Line #%d: Package name cannot be empty\n", line);
                        break;
                }
                if (csv_string_empty(strv[1])) {
                        fprintf(stderr, "Line #%d: Package version cannot be empty\n", line);
                        break;
                }

                t = calloc(1, sizeof(struct source_package_t));
                if (!t) {
                        fprintf(stderr, "Out of memory\n");
                        free(buf);
                        exit(-1);
                }

                d = calloc(1, sizeof(struct FauxData));
                if (!d) {
                        fprintf(stderr, "Out of memory\n");
                        free(t);
                        free(buf);
                        exit(-1);
                }

                t->name = g_strdup(strv[0]);
                t->version = g_strdup(strv[1]);
                if (!csv_string_empty(strv[2])) {
                        d->patched = g_strsplit(strv[2], " ", -1);
                }
                if (!csv_string_empty(strv[3])) {
                        d->ignored = g_strsplit(strv[3], " ", -1);
                }
                t->extra = d;

                cve_add_package_internal(t);
        next:
                free(buf);
                buf = NULL;
                ++line;
        }
        if (buf) {
                free(buf);
                buf = NULL;
        }
end:
        return ret;
}

static void cve_add_package(const char *path)
{
        struct source_package_t *pkg = NULL;
        CvePlugin *pkg_plugin = self_priv->pkg_plugin;

        if (pkg_plugin->scan_package) {
                pkg = pkg_plugin->scan_package(path);
        } else {
                /* only possible for faux */
                load_faux(path);
        }
        if (!pkg) {
                return;
        }
        printf("cve_add_package: calling cve_add_package_internal\n");
        cve_add_package_internal(pkg);
}

static void show_version(void)
{
        const gchar *msg =
            "\
" PACKAGE " " PACKAGE_VERSION
            "\n\
Copyright (C) 2015 Intel Corporation\n\
" PACKAGE_NAME
            " is free software; you can redistribute it and/or modify\n\
it under the terms of the GNU General Public License as published by\n\
the Free Software Foundation; either version 2 of the License, or\n\
(at your option) any later version.";
        fprintf(stderr, "%s\n", msg);
}

static bool hide_patched = false;
static bool show_unaffected = false;
static bool _show_version = false;
static bool skip_update = false;
static gchar *nvds = NULL;
static gchar *forced_type = NULL;
static bool no_html = false;
static bool csv_mode = false;
static char *modified_stamp = NULL;
static gchar *mapping_file = NULL;
static gchar *output_file = NULL;
static gchar *cacert_file = NULL;

static GOptionEntry _entries[] =
    { { "not-patched", 'n', 0, G_OPTION_ARG_NONE, &hide_patched, "Hide patched/addressed CVEs", NULL },
      { "not-affected", 'a', 0, G_OPTION_ARG_NONE, &show_unaffected, "Show unaffected items", NULL },
      { "skip-update", 'u', 0, G_OPTION_ARG_NONE, &skip_update, "Bypass forced updates", NULL },
      { "nvd-dir", 'd', 0, G_OPTION_ARG_STRING, &nvds, "NVD directory in filesystem", NULL },
      { "version", 'v', 0, G_OPTION_ARG_NONE, &_show_version, "Show version", NULL },
      { "type", 't', 0, G_OPTION_ARG_STRING, &forced_type, "Set package type to T", "T" },
      { "no-html", 'N', 0, G_OPTION_ARG_NONE, &no_html, "Disable HTML report", NULL },
      { "modified", 'm', 0, G_OPTION_ARG_STRING, &modified_stamp, "Ignore reports after modification date", "D" },
      { "srpm-dir", 's', 0, G_OPTION_ARG_STRING, &srpm_dir, "Source RPM directory", "S" },
      { "csv", 'c', 0, G_OPTION_ARG_NONE, &csv_mode, "Output CSV formatted data only", NULL },
      { "mapping", 'M', 0, G_OPTION_ARG_STRING, &mapping_file, "Path to a mapping file", NULL },
      { "output-file",
        'o',
        0,
        G_OPTION_ARG_STRING,
        &output_file,
        "Path to the output file (output plugin specific)",
        NULL },
      { "use-fractional-compare",
        'f',
        0,
        G_OPTION_ARG_NONE,
        &use_frac_compare,
        "CVE version string fractional compare",
        NULL },
      { "cacert",
        'C',
        0,
        G_OPTION_ARG_STRING,
        &cacert_file,
        "Path to the combined SSL certificates file (system default is used if not set)",
        NULL },
      {.short_name = 0 } };

/**
 * Attempt to gain the correct packaging plugin for the given path
 */
static CvePlugin *plugin_for_path_abs(GList *plugins, const char *path)
{
        GList *iter = NULL;
        CvePlugin *plugin = NULL;

        if (!plugins || !path) {
                return NULL;
        }

        for (iter = plugins; iter; iter = iter->next) {
                plugin = iter->data;
                printf("trying plugin %s\n",plugin->name);

                if (!(plugin->flags & PLUGIN_TYPE_PACKAGE)) {
                        fprintf(stderr, "plugin_for_path: Incorrect plugin: %s", plugin->name);
                        abort();
                }
                if (plugin->is_package && plugin->is_package(path)) {
                        return plugin;
                }
        }
        return NULL;
}

static CvePlugin *plugin_for_path(GList *plugins, const char *path, bool recurse)
{
        DIR *dir = NULL;
        struct dirent *ent = NULL;
        CvePlugin *ret = NULL;
        struct stat st = {.st_ino = 0 }, stc = {.st_ino = 0 };
        char *p = realpath(path, NULL);

        if (!p) {
                return NULL;
        }

        if (stat(p, &st) != 0) {
                goto end;
        }
        //is it a regular file ?
        if (S_ISREG(st.st_mode)) {
                ret = plugin_for_path_abs(plugins, p);
                goto end;
        } else if (S_ISDIR(st.st_mode) && recurse) {
                if (!(dir = opendir(p))) {
                        goto end;
                }
                while ((ent = readdir(dir))) {
                        autofree(char) *cp = NULL;

                        if (streq(ent->d_name, ".") || streq(ent->d_name, "..")) {
                                continue;
                        }
                        if (!asprintf(&cp, "%s/%s", p, ent->d_name)) {
                                goto end;
                        }
                        if (stat(cp, &stc) != 0) {
                                continue;
                        }
                        if (!S_ISREG(stc.st_mode)) {
                                continue;
                        }

                        ret = plugin_for_path_abs(plugins, cp);
                        if (ret) {
                                break;
                        }
                }
        }
end:
        if (dir) {
                closedir(dir);
        }
        if (p) {
                free(p);
        }
        return ret;
}

static gchar *supported_packages(GList *plugins)
{
        uint len;
        CvePlugin *plugin = NULL;
        gchar *r = NULL;

        if (!plugins || (len = g_list_length(plugins)) < 1) {
                return g_strdup("No supported plugins on this system");
        }

        plugin = g_list_nth_data(plugins, 0);

        if (!asprintf(&r, "%s", plugin->name)) {
                fprintf(stderr, "supported_packages(): Out of memory\n");
                abort();
        }

        for (uint i = 1; i < len; i++) {
                char *t = NULL;

                plugin = g_list_nth_data(plugins, i);
                if (!asprintf(&t, "%s, %s", r, plugin->name)) {
                        fprintf(stderr, "supported_packages(): Out of memory\n");
                        abort();
                }
                free(r);
                r = t;
        }
        return r;
}

static bool cve_locate(const char *path, bool recurse)
{
        struct stat st = {.st_ino = 0 };
        bool ret = false;
        DIR *dir = NULL;
        struct dirent *ent = NULL;
        CvePlugin *pkg_plugin = self_priv->pkg_plugin;

        if (!pkg_plugin || !(pkg_plugin->flags & PLUGIN_TYPE_PACKAGE) || !pkg_plugin->is_package) {
                fprintf(stderr, "Abnormal configuration in plugin\n");
                return false;
        }

        if (lstat(path, &st) != 0) {
                goto end;
        }

        if (S_ISLNK(st.st_mode)) {
                ret = false;
                goto end;
        } else if (S_ISDIR(st.st_mode)) {
                if (!(dir = opendir(path))) {
                        goto end;
                }
                while ((ent = readdir(dir))) {
                        if (!streq(ent->d_name, ".") && !streq(ent->d_name, "..")) {
                                autofree(char) *fullp = NULL;
                                if (!asprintf(&fullp, "%s/%s", path, ent->d_name)) {
                                        goto end;
                                }
                                if (!(cve_is_dir(fullp) && !recurse)) {
                                        cve_locate(fullp, recurse);
                                }
                        }
                }
        } else if (S_ISREG(st.st_mode)) {
                if (pkg_plugin->is_package(path)) {
                        printf("cve_locate: calling cve_add_package\n");
                        cve_add_package(path);
                }
        }

        ret = true;
end:
        if (dir) {
                closedir(dir);
        }
        return ret;
}

/**
 * Main entry.
 */
int main(int argc, char **argv)
{
        autofree(GError) *error = NULL;
        autofree(GOptionContext) *context = NULL;
        autofree(char) *target_sz = NULL;
        autofree(cve_string) *target = NULL;
        autofree(cve_string) *db_path = NULL;
        autofree(CveDB) *cve_db = NULL;
        GList *pkg_plugins = NULL;
        int ret = EXIT_FAILURE;
        CveToolInstance instance = {.pkg_plugin = NULL };
        time_t ti;
        CvePlugin *report = NULL;
        CvePlugin *package = NULL;
        LIBXML_TEST_VERSION
        bool quiet, db_locked;

        /* Public API, shared with Plugins */
        self = &instance.shared;
        /* Private API, only for our own operations */
        self_priv = &instance;

        self->modified = -1;

        context = g_option_context_new(" - cve check tool");
        g_option_context_add_main_entries(context, _entries, NULL);
        if (!g_option_context_parse(context, &argc, &argv, &error)) {
                g_printerr("Invalid options: %s\n", error->message);
                goto cleanup_no_lock;
        }

        quiet = csv_mode || !no_html;
        self->output_file = output_file;
        self->cacert_file = cacert_file;

        if (!csv_mode && self->output_file) {
                quiet = false;
        }

        if (_show_version) {
                show_version();
                ret = EXIT_SUCCESS;
                goto cleanup_no_lock;
        }

        //the defualt path is /home/dafna/NVDS/nvd.db
        db_path = get_db_path(nvds);
        printf("nvds = %s\n",db_path->str);
        printf("nvds = %s\n",nvds);

        if (!db_path) {
                fprintf(stderr, "main(): Can't get db path\n");
                goto cleanup_no_lock;
        }

        db_locked = cve_db_lock_init(db_path->str);
        if (!db_locked) {
                fprintf(stderr, "Not continuing without a database %s\n", "lock");
                goto cleanup_no_lock;
        }

        db_locked = cve_db_read_lock(LOCK_WAIT_SECS);
        if (!db_locked) {
                fputs("Exiting...\n", stderr);
                goto cleanup;
        }

        if (!skip_update) {
                int status = update_required(db_path->str);
                if (status < 0) {
                        fprintf(stderr, "Failed to check if db requires update\n");
                        goto cleanup;
                }
                if (status) {
                        fprintf(stderr, "Update of db forced\n");
                        cve_db_unlock();
                        if (!update_db(quiet, db_path->str, self->cacert_file)) {
                                fprintf(stderr, "DB update failure\n");
                                goto cleanup;
                        }
                }
        } else {
                if (!cve_file_exists(db_path->str)) {
                        fprintf(stderr, "Not continuing without a database %s\n", "file");
                        goto cleanup;
                }
        }

        cve_db = cve_db_new(db_path->str);
        if (!cve_db) {
                fprintf(stderr, "main(): DB initialisation issue\n");
                goto cleanup;
        }

        self->cve_db = cve_db;
        cve_plugin_manager_init();

        pkg_plugins = cve_plugin_get_by_cap(PLUGIN_TYPE_PACKAGE);
        if (!pkg_plugins || g_list_length(pkg_plugins) < 1) {
                fprintf(stderr, "Cannot find any packaging plugins on this system.\n");
                goto cleanup;
        }

        if (srpm_dir) {
                if (!cve_is_dir(srpm_dir)) {
                        fprintf(stderr, "srpm directory does not exist or is not a directory\n");
                        goto cleanup;
                }
                if (!forced_type) {
                        forced_type = "srpm";
                }
        }
        if (forced_type) {
                if (g_str_equal(forced_type, "list")) {
                        /* Print a list of 'em */
                        autofree(gchar) *list = supported_packages(pkg_plugins);
                        printf("Currently supported package types: %s\n", list);
                        goto cleanup;
                } else {
                        package = cve_plugin_get_by_name(forced_type);
                        if (!package) {
                                fprintf(stderr, "Plugin \'%s\' not found.\n", forced_type);
                                goto cleanup;
                        }
                        if (!(package->flags & PLUGIN_TYPE_PACKAGE)) {
                                fprintf(stderr, "Plugin \'%s\' is not a PLUGIN_TYPE_PACKAGE.\n", forced_type);
                                package = NULL;
                                goto cleanup;
                        }
                        self_priv->pkg_plugin = package;
                }
        }

        if (argc != 2) {
                fprintf(stderr, "Usage: %s [path-to-source-spec|path-to-source-list-file]\n", argv[0]);
                goto cleanup;
        }

        if (!cve_file_exists(argv[1])) {
                fprintf(stderr, "%s does not exist\n", argv[1]);
                goto cleanup;
        }

        target_sz = realpath(argv[1], NULL);
        if (!target_sz) {
                goto cleanup;
        }
        target = cve_string_dup(target_sz);

        if (mapping_file) {
                autofree(GKeyFile) *mp = g_key_file_new();
                autofree(gstrv) *keys = NULL;
                self->mapping = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
                if (!g_key_file_load_from_file(mp, mapping_file, 0, &error)) {
                        fprintf(stderr, "Unable to load mapping file: %s\n", error->message);
                        goto cleanup;
                }
                if (!g_key_file_has_group(mp, "Mapping")) {
                        fprintf(stderr, "Mapping file misses [Mapping] group\n");
                        goto cleanup;
                }
                keys = g_key_file_get_keys(mp, "Mapping", NULL, &error);
                if (!keys) {
                        fprintf(stderr, "Unable to load mapping keys: %s\n", error->message);
                        goto cleanup;
                }
                char **c = keys;
                while (*c) {
                        autofree(gchar) *val = g_key_file_get_string(mp, "Mapping", *c, &error);
                        if (!val) {
                                fprintf(stderr, "Unable to load mapping string: %s\n", error->message);
                                goto cleanup;
                        }
                        /* Reverse the mapping */
                        g_hash_table_insert(self->mapping, g_strdup(val), g_strdup(*c));
                        ++c;
                }
        }

        if (!forced_type) {
                package = plugin_for_path(pkg_plugins, target->str, false);
                if (package) {
                        self_priv->pkg_plugin = package;
                }
        }

        if (modified_stamp) {
                ti = curl_getdate(modified_stamp, NULL);
                if (ti <= 0) {
                        fprintf(stderr, "Invalid date\n");
                        goto cleanup;
                }
                self->modified = (int64_t)ti;
        }

        self->hide_patched = hide_patched;
        self->show_unaffected = show_unaffected;
        self->db = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, package_free);
        self->bdb = NULL;

        if (is_package_list(target)) {
                /* Packages file */
                ssize_t read = -1;
                size_t buf_size = 0;
                FILE *fp = NULL;
                char *buf = NULL;
                autofree(char) *basedirsz = NULL;

                fp = fopen(target->str, "r");
                if (!fp) {
                        fprintf(stderr, "Unable to open file for reading: %s\n", strerror(errno));
                        goto cleanup;
                }

                basedirsz = cve_get_file_parent(target->str);
                if (!basedirsz) {
                        fprintf(stderr, "Error locating file parent: %s\n", target->str);
                        goto cleanup;
                }

                while ((read = getline(&buf, &buf_size, fp) > 0)) {
                        autofree(char) *path = NULL;
                        buf = g_strchomp(buf);

                        if (streq(buf, "")) {
                                free(buf);
                                buf = NULL;
                                continue;
                        }

                        /* Tab delimited files mean we don't look through git trees */
                        if (str_contains(buf, "\t")) {
                                autofree(gstrv) *splits = g_strsplit(buf, "\t", 3);
                                struct source_package_t *t = NULL;

                                if (srpm_dir) {
                                        if (!package) {
                                                fprintf(stderr, "No package plugin configured, bailing\n");
                                                goto cleanup;
                                        }
                                        t = package->scan_archive(srpm_dir, splits[0], splits[1], splits[2]);
                                        if (!t) {
                                                goto clean;
                                        }
                                } else {
                                        if (!package) {
                                                fprintf(stderr, "No package plugin configured, bailing\n");
                                                goto cleanup;
                                        }
                                        t = calloc(1, sizeof(struct source_package_t));
                                        if (!t) {
                                                goto clean;
                                        }
                                        t->path = NULL;
                                        t->name = g_strdup(splits[0]);
                                        t->version = g_strdup(splits[1]);
                                }
                                cve_add_package_internal(t);
                                goto clean;
                        }
                        /* try directory above *first* as this is the norm */
                        if (!asprintf(&path, "%s/../%s", basedirsz, buf)) {
                                fprintf(stderr, "main(): Out of memory\n");
                                goto cleanup;
                        }
                        printf("path= %s\n",path);

                        if (!cve_file_exists(path)) {
                                free(path);
                                /* Fall back to building from current directory */
                                if (!asprintf(&path, "%s/%s", basedirsz, buf)) {
                                        fprintf(stderr, "main(): Out of memory\n");
                                        goto cleanup;
                                }
                        }
                        printf("path= %s\n",path);
                        if (!cve_file_exists(path)) {
                                fprintf(stderr, "Warning: Not found: %s\n", path);
                                goto clean;
                        }

                        /* Attempt to determine type.. */
                        if (!package) {
                                package = plugin_for_path(pkg_plugins, path, true);
                                if (!package) {
                                        fprintf(stderr, "Unable to determine package type, bailing\n");
                                        free(buf);
                                        goto cleanup;
                                }
                                self_priv->pkg_plugin = package;
                        }
                        cve_locate(path, false);
                clean:
                        free(buf);
                        buf = NULL;
                }//end of while
                if (buf) {
                        free(buf);
                        buf = NULL;
                }

        } else {//is_package_list
                if (!package) {
                        fprintf(stderr, "Unsupported package type\n");
                        goto cleanup;
                }
                self_priv->pkg_plugin = package;
                if (cve_is_dir(target->str)) {
                        cve_locate(target->str, true);
                } else {
                        cve_add_package(target->str);
                }
        }

        //static CveCheckTool *self;

        gint size = g_hash_table_size(self->db);
        if (size == 0) {
                fprintf(stderr, "No source files were encountered, aborting\n");
                goto cleanup;
        }
        /* Consider a verbosity flag... */
        if (!quiet) {
                fprintf(stderr, "Scanned %d source file%s\n", size, size > 1 ? "s" : "");
        }

        /* TODO: Switch to single output mode, with a report type set in
         * config and/or flags, i.e. -r html (preserve csv option though)
         */
        if (csv_mode) {
                report = cve_plugin_get_by_name("csv");
        } else if (!no_html) {
                report = cve_plugin_get_by_name("html");
        } else {
                printf("in cli mode\n");
                report = cve_plugin_get_by_name("cli");
        }

        if (!report || !report->report) {
                fprintf(stderr, "No usable output module\n");
                goto cleanup;
        }

        printf("main: calling the report function of the report %p\n",report);
        if (!report->report(self)) {
                fprintf(stderr, "Report generation failed\n");
                goto cleanup;
        }

        ret = EXIT_SUCCESS;

cleanup:
        cve_db_lock_fini();

cleanup_no_lock:
        if (pkg_plugins) {
                g_list_free(pkg_plugins);
        }
        if (self->db) {
                g_hash_table_unref(self->db);
        }
        if (self->bdb) {
                g_hash_table_unref(self->bdb);
        }
        if (self->mapping) {
                g_hash_table_unref(self->mapping);
        }
        cve_plugin_manager_destroy();

        xmlCleanupParser();
        return ret;
}

/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 8
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * vi: set shiftwidth=8 tabstop=8 expandtab:
 * :indentSize=8:tabSize=8:noTabs=true:
 */
